package EjerciciosEnClase.EjercicioCiudades;

import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

public class Ciudad {

	private String nombre;
	private Set<Lugar> lugares;
	private List<Recorrido> recorridos;
	
	public Ciudad(String nombre){
		this.setNombre(nombre);
		this.lugares = new HashSet<Lugar>();
		this.recorridos = new LinkedList<Recorrido>();
	}
	
	public void agregarLugares(Lugar nuevoLugar){
		this.lugares.add(nuevoLugar);
	}
	
	public void agregarLugares(List<Lugar> nuevosLugares){
		this.lugares.addAll(nuevosLugares);
	}

	public void setNombre(String nombre) {
		this.nombre = nombre;
	}

	public String getNombre() {
		return nombre;
	}
	
	public Set<Lugar> getLugares(){
		return this.lugares;
	}
	
	public List<Recorrido> getRecorridos(){
		return this.recorridos;
	}
	
	/**
	 * @post Devuelve un conjunto de "Lugares" ordenados por nombre.
	 */
	public SortedSet<Lugar> obtenerLugaresOrdenadosPorNombre(){
		SortedSet<Lugar> listaDeLugaresOrdenada = new TreeSet<Lugar>(); 
		listaDeLugaresOrdenada.addAll(this.lugares);
		return listaDeLugaresOrdenada;	
	}
	
	/**
	 * @post Devuelve un conjunto de "Lugares" ordenado por id.
	 */
	public SortedSet<Lugar> obtenerLugaresOrdenadosPorId(){
		Comparator<Lugar> comparator = new ComparadorDeLugaresPorId();
		SortedSet<Lugar> listaDeLugaresOrdenada = new TreeSet<Lugar>(comparator);
		listaDeLugaresOrdenada.addAll(this.lugares);
		return listaDeLugaresOrdenada;	
	}
	
	/**
	 * @post Devuelve una Lista de "Lugares" que contengan una etiqueta pedida.
	 */
	public List<Lugar> getLugaresPorEtiqueta(Etiquetas etiquetaABuscar){
		List<Lugar> listaDeLugaresPorEtiqueta = new LinkedList<Lugar>();
		Iterator<Lugar> itLugares = this.lugares.iterator();
		
		while(itLugares.hasNext()){
			Lugar siguienteElementoDelIterador = itLugares.next();
			if((siguienteElementoDelIterador.getEtiquetas()).contains(etiquetaABuscar)){
				listaDeLugaresPorEtiqueta.add(siguienteElementoDelIterador);
			}
		}
		return listaDeLugaresPorEtiqueta;
	}
	
	/**
	 * @post Devuelve un conjunto de "Lugares" que contenga al menos una etiqueta pedida.
	 */
	public Set<Lugar> getLugaresPorEtiqueta(List<Etiquetas> listaEtiquetas){
		Set<Lugar> listaDeLugaresPorEtiqueta = new HashSet<Lugar>();
		Iterator<Etiquetas> itEtiquetas = listaEtiquetas.iterator();
		while(itEtiquetas.hasNext()){
			Etiquetas primerEtiquetaDeLaLista = itEtiquetas.next();
			listaDeLugaresPorEtiqueta.addAll(this.getLugaresPorEtiqueta(primerEtiquetaDeLaLista));
		}
		return listaDeLugaresPorEtiqueta;
	}
	
	/**
	 * @post: Devuelve los Lugares que estén localizados en la región delimitada por
	 *		  los valores dados.
	 */
	public Set<Lugar> getLugaresEnRegion(double latitudMaxima, double latitudMinima,
										double longitudMaxima, double longitudMinima){
		Set<Lugar> listaDeLugaresBuscados = new HashSet<Lugar>();
		Iterator<Lugar>itLugares = this.lugares.iterator();
		while (itLugares.hasNext()) {
			Lugar proximoLugar = itLugares.next();
			if((latitudMaxima > proximoLugar.getLatitud()) &&
				(proximoLugar.getLatitud() > latitudMinima) &&
				(longitudMaxima > proximoLugar.getLatitud()) &&
				(proximoLugar.getLatitud() > latitudMinima)){
				listaDeLugaresBuscados.add(proximoLugar);
			}
		}
		return listaDeLugaresBuscados;
	}
	
	/**
	* @post: Devuelve un Mapa en el que se asocia para cada Etiqueta la cantidad de
	*		 Lugares de la Ciudad que la poseen.
	*/
	public Map<Etiquetas, Integer> contarLugaresPorEtiqueta(){
		Map<Etiquetas, Integer> mapaDeLugares = new HashMap<Etiquetas, Integer>();
		List<Etiquetas> listaEtiquetas = this.generarListaConEtiquetas();
		Iterator<Etiquetas> itListaEtiquetas = listaEtiquetas.iterator();
		while(itListaEtiquetas.hasNext()){
			Etiquetas proximaEtiqueta = itListaEtiquetas.next();
			mapaDeLugares.put(proximaEtiqueta, (this.getLugaresPorEtiqueta(proximaEtiqueta)).size());
		}
		return mapaDeLugares;
	}

	/**
	* @post: Devuelve un Mapa en el que se asocia para cada Etiquetas la lista
	*		 de Lugares que poseen esta etiqueta. Si un Lugar posee más de una
	*		 Etiqueta estará en más de una asociación.
	* @return: Map<Etiquetas, List<Lugar>
	*/
	public Map<Etiquetas, List<Lugar>> getLugaresPorEtiqueta(){
		Map<Etiquetas, List<Lugar>> mapaDeLugares = new HashMap<Etiquetas, List<Lugar>>();
		List<Etiquetas> listaEtiquetas = this.generarListaConEtiquetas();
		Iterator<Etiquetas> itListaEtiquetas = listaEtiquetas.iterator();
		while(itListaEtiquetas.hasNext()){
			Etiquetas proximaEtiqueta = itListaEtiquetas.next();
			mapaDeLugares.put(proximaEtiqueta, (this.getLugaresPorEtiqueta(proximaEtiqueta)));
		}
		return mapaDeLugares;
	}
	
	/**
	 *  @post: Devuelve una lista de Etiquetas.
	 */
	private List<Etiquetas> generarListaConEtiquetas() {
		List<Etiquetas> listaEtiquetas = new LinkedList<Etiquetas>();
		listaEtiquetas.add(Etiquetas.COMERCIO);
		listaEtiquetas.add(Etiquetas.CULTURA);
		listaEtiquetas.add(Etiquetas.HISTORIA);
		listaEtiquetas.add(Etiquetas.INFANTIL);
		listaEtiquetas.add(Etiquetas.RECREACION);
		return listaEtiquetas;
	}
	
	/**
	* @pre : idLugares contiene los valores de id de Lugares que pertenecen a la Ciudad
	* @post: Crea un nuevo Recorrido con el nombre indicado, agregándole los Lugares
	*		indicados a partir de la Lista idLugares, y lo agrega a la Ciudad.
	*/
	public void agregarRecorrido(String nombre, List<Integer> idLugares){
		Recorrido nuevoRecorrido = new Recorrido(nombre);
		Iterator<Lugar> itLugar = this.lugares.iterator();
		while(itLugar.hasNext()){
			Lugar proximoLugar = itLugar.next();
			if(idLugares.contains(proximoLugar.getId())){
				nuevoRecorrido.setLugares(proximoLugar);
			}
		}
		this.recorridos.add(nuevoRecorrido);
	}
	
	/**
	* @post: Devuelve el Recorrido identificado por nombre que posee la Ciudad o
	*		 null si no existe.
	*/
	public Recorrido getRecorrido(String nombre){
		Iterator<Recorrido> itRecorridos = this.recorridos.iterator();
		while (itRecorridos.hasNext()) {
			Recorrido proximoRecorrido = itRecorridos.next();
			if(nombre.equals(proximoRecorrido.getNombre())){
				return proximoRecorrido;
			}
		}
		return null;
	}
}
